#pragma once

#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
#include <memory>
#include <functional>
#include <string>

#include "logger.hpp"

namespace xu
{
    class Registration
    {
    public:
        using ptr = std::shared_ptr<Registration>;
        Registration(const std::string &host, int ttl = 3)
            : _client(std::make_shared<etcd::Client>(host)),
              _keep_alive(_client->leasekeepalive(ttl).get()),
              _lease_id(_keep_alive->Lease())
        {
        }

        bool AddKV(const std::string &key, const std::string val)
        {
            auto resp = _client->put(key, val, _lease_id).get();
            if (resp.is_ok() == false)
            {
                LOG_ERROR("新增数据失败:{}", resp.error_message());
                return false;
            }
            LOG_INFO("新增数据成功");
            return true;
        }

    private:
        std::shared_ptr<etcd::Client> _client;
        std::shared_ptr<etcd::KeepAlive> _keep_alive;
        int64_t _lease_id;
    };

    class Discovery
    {
    public:
        using ptr = std::shared_ptr<Discovery>;
        using NotifyCallback = std::function<void(const std::string &, const std::string &)>;
        Discovery(const std::string &host, const std::string &base_dir,
                  NotifyCallback put, NotifyCallback del)
            : _client(std::make_shared<etcd::Client>(host)),
              _base_dir(base_dir),
              _put(put),
              _del(del)
        {
            auto resp = _client->ls(_base_dir).get();
            if (resp.is_ok() == false)
            {
                LOG_ERROR("获取键值对数据失败:{}", resp.error_message());
            }
            else
            {
                int sz = resp.keys().size();
                for (int i = 0; i < sz; ++i)
                {
                    if (_put)
                    {
                        _put(resp.key(i), resp.value(i).as_string());
                    }
                }
            }

             _watcher = std::make_shared<etcd::Watcher>(*_client.get(), _base_dir, std::bind(&Discovery::Callback, this, std::placeholders::_1), true);
        }

        ~Discovery()
        {
            _watcher->Cancel();
        }

    private:
        void Callback(const etcd::Response &resp)
        {
            if (resp.is_ok() == false)
            {
                LOG_ERROR("收到一个错误的事件通知:{}", resp.error_message());
                return;
            }
            for (auto const &ev : resp.events())
            {
                if (ev.event_type() == etcd::Event::EventType::PUT)
                {
                    if (_put)
                    {
                        _put(ev.kv().key(), ev.kv().as_string());
                    }
                }
                else if (ev.event_type() == etcd::Event::EventType::DELETE_)
                {
                    if (_del)
                    {
                        _del(ev.prev_kv().key(), ev.prev_kv().as_string());
                    }
                }
            }
        }

    private:
        std::shared_ptr<etcd::Client> _client;
        std::shared_ptr<etcd::Watcher> _watcher;
        std::string _base_dir;
        NotifyCallback _put;
        NotifyCallback _del;
    };
}
